// 确保在Windows平台下包含必要的库文件
#ifdef _WIN32
#pragma comment(lib, "ws2_32.lib") // 链接Windows Socket库(ws2_32.lib),提供网络编程功能
#endif

// 包含Windows套接字编程所需的头文件
#include <winsock2.h> // 必须在其他Windows头文件之前包含,提供网络编程API
#include <stdio.h>    // 提供标准输入输出函数
#include <string.h>   // 提供字符串处理函数
#include <process.h>  // 提供线程相关函数,如_beginthreadex

// 定义程序中使用的常量
#define BUFF_LENGTH 1024   // 消息缓冲区大小(字节)
#define MAX_CLIENTS 10     // 最大允许的客户端连接数
#define USERNAME_LENGTH 32 // 用户名最大长度

// 定义客户端结构体,用于存储客户端信息
typedef struct
{
    SOCKET socket;                  // 客户端的套接字
    char username[USERNAME_LENGTH]; // 客户端的用户名
} Client;

// 全局变量声明
Client clients[MAX_CLIENTS]; // 存储所有已连接客户端的数组
int client_count = 0;        // 当前已连接的客户端数量
CRITICAL_SECTION cs;         // 临界区对象,用于多线程同步

// 处理客户端连接的线程函数
// 参数arg: 指向客户端套接字的指针
unsigned __stdcall HandleClient(void *arg)
{
    SOCKET client_socket = *(SOCKET *)arg;
    free(arg); // 释放动态分配的内存

    char username[USERNAME_LENGTH]; // 存储用户名
    char buffer[BUFF_LENGTH];       // 消息缓冲区
    int recv_len;                   // 接收到的消息长度

    // 请求并接收用户名
    char welcome_msg[] = "Please enter your username: ";
    send(client_socket, welcome_msg, strlen(welcome_msg), 0);

    recv_len = recv(client_socket, username, USERNAME_LENGTH - 1, 0);
    username[recv_len] = '\0'; // 确保字符串正确终止

    // 进入临界区,保护共享资源
    EnterCriticalSection(&cs);

    // 保存新客户端信息到全局数组
    clients[client_count].socket = client_socket;
    strcpy(clients[client_count].username, username);
    client_count++;

    // 向所有其他客户端广播新用户加入的消息
    char join_msg[BUFF_LENGTH];
    sprintf(join_msg, "User %s has joined the chat room\n", username);
    for (int i = 0; i < client_count - 1; i++)
    {
        send(clients[i].socket, join_msg, strlen(join_msg), 0);
    }

    LeaveCriticalSection(&cs); // 离开临界区
    printf("%s", join_msg);    // 在服务器端显示加入消息

    // 主消息处理循环
    while ((recv_len = recv(client_socket, buffer, BUFF_LENGTH - 1, 0)) > 0)
    {
        buffer[recv_len] = '\0'; // 确保消息正确终止

        // 构造带用户名的完整消息
        char broadcast_msg[BUFF_LENGTH];
        sprintf(broadcast_msg, "%s says: %s", username, buffer);

        // 进入临界区,准备广播消息
        EnterCriticalSection(&cs);

        // 向所有其他客户端广播消息
        for (int i = 0; i < client_count; i++)
        {
            if (clients[i].socket != client_socket)
            {
                send(clients[i].socket, broadcast_msg, strlen(broadcast_msg), 0);
            }
        }

        LeaveCriticalSection(&cs);
        printf("%s", broadcast_msg); // 在服务器端显示消息
    }

    // 处理客户端断开连接
    EnterCriticalSection(&cs);

    // 在客户端数组中查找断开连接的客户端
    int disconnect_index = -1;
    for (int i = 0; i < client_count; i++)
    {
        if (clients[i].socket == client_socket)
        {
            disconnect_index = i;
            break;
        }
    }

    if (disconnect_index != -1)
    {
        // 构造用户离开消息
        char leave_msg[BUFF_LENGTH];
        sprintf(leave_msg, "User %s has left the chat room\n", clients[disconnect_index].username);

        // 从数组中移除断开的客户端
        for (int i = disconnect_index; i < client_count - 1; i++)
        {
            clients[i] = clients[i + 1];
        }
        client_count--;

        // 向其他所有客户端发送离开消息
        for (int i = 0; i < client_count; i++)
        {
            send(clients[i].socket, leave_msg, strlen(leave_msg), 0);
        }

        printf("%s", leave_msg); // 在服务器端显示离开消息
    }

    LeaveCriticalSection(&cs);
    closesocket(client_socket); // 关闭客户端套接字
    return 0;
}

int main()
{
    // 初始化Windows套接字环境,使用2.2版本
    WSADATA wsa_Data;
    WSAStartup(MAKEWORD(2, 2), &wsa_Data);

    // 初始化用于线程同步的临界区对象
    InitializeCriticalSection(&cs);

    // 创建服务器套接字,使用TCP协议
    SOCKET server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (server_socket == INVALID_SOCKET)
    {
        printf("socket failed\n");
        WSACleanup();
        return 1;
    }

    // 配置服务器地址结构体
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;                // 使用IPv4地址族
    server_addr.sin_port = htons(8080);              // 设置服务器端口为8080
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有可用网络接口

    // 设置套接字选项,允许地址重用
    int opt = 1;
    if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)) == SOCKET_ERROR)
    {
        printf("setsockopt failed\n");
        closesocket(server_socket);
        WSACleanup();
        return 1;
    }

    // 将套接字绑定到配置的地址和端口
    if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
    {
        printf("bind failed\n");
        closesocket(server_socket);
        WSACleanup();
        return 1;
    }

    // 开始监听连接请求
    if (listen(server_socket, 5) == SOCKET_ERROR) // 设置待处理连接队列最大长度为5
    {
        printf("listen failed\n");
        closesocket(server_socket);
        WSACleanup();
        return 1;
    }

    // 获取并显示服务器的IP地址信息
    char host_name[256];
    gethostname(host_name, sizeof(host_name));
    struct hostent *host_info = gethostbyname(host_name);
    if (host_info)
    {
        struct in_addr **addr_list = (struct in_addr **)host_info->h_addr_list;
        for (int i = 0; addr_list[i] != NULL; i++)
        {
            printf("Server is running on IP: %s, port: 8080\n", inet_ntoa(*addr_list[i]));
        }
    }
    printf("Waiting for connections...\n");

    // 服务器主循环,持续接受新的客户端连接
    while (1)
    {
        // 接受新的客户端连接
        struct sockaddr_in client_addr;
        int addr_len = sizeof(client_addr);
        SOCKET client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &addr_len);
        if (client_socket == INVALID_SOCKET)
        {
            printf("accept failed\n");
            continue;
        }

        // 显示新连接的客户端信息
        printf("New connection from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

        // 检查是否达到最大客户端数量限制
        if (client_count >= MAX_CLIENTS)
        {
            const char *msg = "Server has reached maximum connections!";
            send(client_socket, msg, strlen(msg), 0);
            closesocket(client_socket);
            continue;
        }

        // 为新客户端创建处理线程
        SOCKET *pclient = (SOCKET *)malloc(sizeof(SOCKET));
        *pclient = client_socket;
        unsigned thread_id;
        _beginthreadex(NULL, 0, HandleClient, pclient, 0, &thread_id);
    }

    // 清理资源
    DeleteCriticalSection(&cs); // 删除临界区对象
    closesocket(server_socket); // 关闭服务器套接字
    WSACleanup();               // 清理Windows套接字环境
    return 0;
}